//
//  main.c
//  read_elf
//
//  Created by apple on 2021/12/18.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "elf.h"


typedef struct TGY_Elf63_Shdr{
    
    char* section_name;
    Elf64_Shdr shdr;
    struct TGY_Elf63_Shdr *next;
}TGY_Elf63_Shdr;

//extern char __executable_start[];

void tgy_free(void **point) {
    
    if (*point == NULL) {
        
        return;
    }
    
    free(*point);
    *point = NULL;
}

void seekToLoc(FILE *file,size_t loc) {
    
    fseek(file, 0, SEEK_SET);
    fseek(file, loc, SEEK_SET);
}

TGY_Elf63_Shdr * find_elf64_shdr_by_section_name(TGY_Elf63_Shdr *elf_header, const char *section_name) {
    
    if (section_name == NULL) {
        
        return NULL;
    }
    
    TGY_Elf63_Shdr *tmp_header = elf_header;
    
    while (tmp_header != NULL) {
        
        
        if (strcmp(tmp_header->section_name, section_name) == 0) {
            
            return tmp_header;
        }
        
        tmp_header = tmp_header->next;
    }
    
    return NULL;
}

void read_symbol(TGY_Elf63_Shdr *symbol_header,FILE *elfFile,const char *str_content) {
    
    if (symbol_header == NULL || elfFile == NULL) {
        
        return;
    }
    
    seekToLoc(elfFile, symbol_header->shdr.sh_offset);
    
    int count = symbol_header->shdr.sh_size / symbol_header->shdr.sh_entsize;
    
//    int sym_size = sizeof(Elf64_Sym);
    Elf64_Sym sym;
    
    for (int i = 0; i < count; i++) {
        
        
        int result = fread(&sym, 1, sizeof(Elf64_Sym), elfFile);
        
        if (result < 0) {
            
            fprintf(stderr, "fread error");
            return;
        }
        
        char *name =  str_content + sym.st_name;
        printf("symbol:%s ",name);
        
        uint8_t sym_bind = ELF64_ST_BIND(sym.st_info);
        
        //#define STB_LOCAL    0        /* Local symbol */
        //#define STB_GLOBAL    1        /* Global symbol */
        //#define STB_WEAK    2        /* Weak symbol */
        
        switch (sym_bind) {
            case 0:
                printf(" STB_LOCAL");
                break;
            case 1:
                printf(" STB_GLOBAL");
                break;
            case 2:
                printf(" STB_WEAK");
                break;
            default:
                printf(" other");
                break;
        }
        
        sym.st_shndx;
        
//#define SHN_UNDEF    0        /* Undefined section */
//#define SHN_ABS        0xfff1        /* Associated symbol is absolute */
//#define SHN_COMMON    0xfff2        /* Associated symbol is common */
        switch (sym.st_shndx) {
            case SHN_UNDEF:
                printf(" SHN_UNDEF");
                break;
            case SHN_ABS:
                printf(" SHN_ABS");
                break;
            case SHN_COMMON:
                printf(" SHN_COMMON");
            default:
                printf(" %d offsize:%d ",sym.st_shndx, sym.st_value);
                break;
        }
        
        uint8_t sym_type = ELF64_ST_TYPE(sym.st_info);
        
        
        
        switch(sym_type) {
                
            case STT_OBJECT:
                printf(" STT_OBJECT \n");
                break;
            case STT_FUNC:
                printf(" STT_FUNC \n");
                break;
            case STT_SECTION:
                printf(" STT_SECTION \n");
                break;
            case STT_FILE:
                printf(" STT_FILE \n");
                break;
            default:
                printf(" other \n");
        }
        
        
    }
    
    
    
    
}

char *read_elf_str(TGY_Elf63_Shdr *str_tab_header,FILE *elfFile) {
    
    char *strs = malloc(str_tab_header->shdr.sh_size);
    
    seekToLoc(elfFile, str_tab_header->shdr.sh_offset);
    fread(strs, 1, str_tab_header->shdr.sh_size, elfFile);
    
    return strs;
}

void read_elf(const char *path) {

    FILE *elfFile = NULL;
    Elf64_Ehdr elfHeader;
    size_t elfHeaderSize;
    
    
    Elf64_Shdr strSection;
    
    size_t shStrFileOffset;
    
    size_t result;
    
    TGY_Elf63_Shdr *shdr_head = NULL;
    
    const char *str_content = NULL;
    
    
    elfFile = fopen(path, "rb");
    
    if (elfFile == NULL) {
        
        fprintf(stderr, "%s path error",path);
        goto end;
    }
    
    
    elfHeaderSize = sizeof(Elf64_Ehdr);
    
    result = fread(&elfHeader, 1, elfHeaderSize, elfFile);
    
    if (result != elfHeaderSize) {
        
        goto end;
    }
    
    // 获取shstr section offset
    shStrFileOffset = elfHeader.e_shoff + elfHeader.e_shentsize * elfHeader.e_shstrndx;
    
    seekToLoc(elfFile, shStrFileOffset);
    
    result = fread(&strSection, 1, elfHeader.e_shentsize, elfFile);
    
    if (result != elfHeader.e_shentsize) {
        
        goto end;
    }
    
    
    char *strContent = malloc(strSection.sh_size);
    
    seekToLoc(elfFile, strSection.sh_offset);
    fread(strContent, 1, strSection.sh_size, elfFile);
    
    seekToLoc(elfFile, elfHeader.e_shoff);
    
 
    TGY_Elf63_Shdr *shdr_last = NULL;
    
    for (int i = 0; i < elfHeader.e_shnum; i++) {
        
        TGY_Elf63_Shdr *tmp_elf64_shdr = malloc(sizeof(TGY_Elf63_Shdr));
        
        result = fread(&tmp_elf64_shdr->shdr, 1, elfHeader.e_shentsize, elfFile);
        
        if (result <= 0) {
            
            goto end;
        }
        tmp_elf64_shdr->section_name = strContent + tmp_elf64_shdr->shdr.sh_name;

        if (shdr_last != NULL) {
            
            shdr_last->next = tmp_elf64_shdr;
        }else {
            
            shdr_head = tmp_elf64_shdr;
        }
        
        shdr_last = tmp_elf64_shdr;
    }
    
    str_content = read_elf_str(find_elf64_shdr_by_section_name(shdr_head, ".strtab"), elfFile);
    
    read_symbol(find_elf64_shdr_by_section_name(shdr_head, ".symtab"), elfFile,str_content);
    
    
end:
    if (elfFile) {
        
        fclose(elfFile);
    }
    
    tgy_free(&strContent);
    
}

__attribute__((weak)) int age;

//int age = 20;
int age = 10;

//void run() __attribute__((weak));

#pragma message("tgy-option")


int main(int argc, const char * argv[]) {
    // insert code here...
    
//    printf("%x",__executable_start);
    read_elf("/Users/apple/Documents/c++/os_linked_load_library/read_elf/SimpleSection.o");
    
   
    
    
    return 0;
}
